package com.nx.fsm.engine;

import com.nx.fsm.action.ActionProvider;
import com.nx.fsm.action.FsmAction;
import com.nx.fsm.action.impl.DefaultActionProviderImpl;
import com.nx.fsm.context.FsmContext;
import com.nx.fsm.context.FsmNodeResult;
import com.nx.fsm.context.FsmRole;
import com.nx.fsm.exception.ErrorCode;
import com.nx.fsm.exception.FsmException;
import com.nx.fsm.job.JobConfig;
import com.nx.fsm.job.JobConfigProvider;
import com.nx.fsm.model.*;
import com.nx.fsm.transaction.TransactionConfig;
import com.nx.fsm.transaction.TransactionConfigProvider;
import com.nx.fsm.transaction.TransactionMsg;
import com.nx.fsm.transaction.TransactionMsgProvider;
import com.nx.fsm.transaction.impl.DefaultTransactionMsgProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class DefaultFsmEngine implements FsmEngine {

    private final static Logger logger = LoggerFactory.getLogger(DefaultFsmEngine.class);

    /**
     * 状态机配置
     */
    private HashMap<Integer, FsmTemplate> fsmMap = new HashMap<>();
    private HashSet<JobConfig> jobConfigs;  //离线配置
    private HashSet<TransactionConfig> transactionConfigs;

    private FsmMappingProvider fsmMappingProvider;//状态机配置
    private FsmSelector fsmSelector;//状态机选择器
    private ActionProvider actionProvider;//Action构造器


    /**
     * 离线配置-可选参数
     */
    private boolean useJob = false;//加载离线配置
    private JobConfigProvider jobConfigProvider;//离线配置提供者
    /**
     * 事务消息-可选参数
     */
    private boolean useTransactionConfig = false;//加载事务消息
    private TransactionConfigProvider transactionConfigProvider;// 事务消息提供者-配置
    private TransactionMsgProvider transactionMsgProvider;// 事务消息提供者


    /**
     * 初始化状态机配置 fsmMap
     */
    @Override
    public void init() {
        if (actionProvider == null) {
            actionProvider = new DefaultActionProviderImpl();
        }
        if (fsmSelector == null) {
            throw new FsmException(ErrorCode.FSM_SELECTOR_NOT_FOUND);
        }
        /*
         * 获取数据库配置 & 验证配置
         */
        List<FsmMapping> fsmMappings = fsmMappingProvider.getFsmMapping();
        validationConfig(fsmMappings);

        if (useJob) {
            // 开启job离线
            if (jobConfigProvider == null) {
                throw new FsmException(ErrorCode.JOB_PROVIDER_NOT_FOUND);
            }
            jobConfigs = jobConfigProvider.getJobConfigs();
        }
        if (useTransactionConfig) {
            // 开启事务消息
            if (transactionConfigProvider == null) {
                throw new FsmException(ErrorCode.MSG_PROVIDER_NOT_FOUND);
            }
            if (transactionMsgProvider == null) {
                transactionMsgProvider = new DefaultTransactionMsgProvider();
            }
            transactionConfigs = transactionConfigProvider.getTransactionConfigs();
        }

        /*
         * 从数据库映射关系-->状态机配置实体
         */
        Map<Integer, List<FsmNode>> fsmNodeMap = new HashMap<>();
        for (FsmMapping fsmMapping : fsmMappings) {
            try {
                FsmAction action = actionProvider.getBean(fsmMapping.getHandler());
                NodeType nodeType = NodeType.getById(fsmMapping.getNodeType());
                FsmNode fsmNode = new FsmNode(fsmMapping.getOpType(), fsmMapping.getRole(), fsmMapping.getSourceStatus(), fsmMapping.getTargetStatus(), nodeType, action);
                if (useJob && jobConfigs != null) {
                    // 离线配置
                    fsmNode.setJobConfigs(getJobConfigByCodes(fsmMapping.getJobCodes()));
                }
                if (useTransactionConfig && transactionConfigs != null) {
                    // 事务消息配置
                    fsmNode.setTransactionConfigs(getTransactionConfigByCodes(fsmMapping.getTransactionCodes()));
                }
                fsmNodeMap.computeIfAbsent(fsmMapping.getFsmType(), k -> new ArrayList<>());
                fsmNodeMap.get(fsmMapping.getFsmType()).add(fsmNode);
            } catch (Exception ex) {
                logger.error("desc=init fsm engine handler, name:{} ex={}", fsmMapping.getHandler(), ex.getMessage());
            }
        }

        /*
         * 初始化状态机配置实体
         */
        for (Map.Entry<Integer, List<FsmNode>> entry : fsmNodeMap.entrySet()) {
            FsmTemplate fsmTemplate = new FsmTemplate(entry.getKey(), entry.getValue());
            fsmMap.put(fsmTemplate.getType(), fsmTemplate);
        }
    }

    /**
     * 执行状态机
     *
     * @param context
     */
    @Override
    public void perform(FsmContext context) {
        FsmNode fsmNode = findFsmNode(context);
        /*
         * 执行校验逻辑
         */
        fsmNode.getAction().check(context);
        FsmNodeResult fsmNodeResult = new FsmNodeResult(fsmNode);
        if (useTransactionConfig && fsmNode.getTransactionConfigs() != null) {
            HashSet<TransactionMsg> transactionMsgs = transactionMsgProvider.getTransactionMsgs(fsmNode.getTransactionConfigs(), context, fsmNode.getTargetStatus());
            fsmNodeResult.setTransactionMsgs(transactionMsgs);
        }
        /*
         * 执行action
         */
        fsmNode.getAction().execute(context, fsmNodeResult);
    }

    @Override
    public FsmNodeResult getPerformResult(FsmContext context) {
        FsmNode fsmNode = findFsmNode(context);
        FsmNodeResult fsmNodeResult = new FsmNodeResult(fsmNode);
        if (useTransactionConfig && fsmNode.getTransactionConfigs() != null) {
            HashSet<TransactionMsg> transactionMsgs = transactionMsgProvider.getTransactionMsgs(fsmNode.getTransactionConfigs(), context, fsmNode.getTargetStatus());
            fsmNodeResult.setTransactionMsgs(transactionMsgs);
        }
        return fsmNodeResult;
    }

    /**
     * 查找配置节点
     *
     * @param context
     * @return
     */
    private FsmNode findFsmNode(FsmContext context) {
        Integer status = context.getStatus();
        FsmRole role = context.getRole();
        Integer opType = context.getOpType();
        /*
         * 定位配置项
         */
        Integer fsmType = fsmSelector.selectFsmType(context);
        FsmTemplate fsmTemplate = fsmMap.get(fsmType);
        if (fsmTemplate == null) {
            throw new FsmException(ErrorCode.FSM_TYPE_NOT_FOUND, status, role.getId(), opType);
        }
        FsmNode fsmNode = fsmTemplate.getFsmNode(status, opType, role);
        if (fsmNode == null) {
            throw new FsmException(ErrorCode.FSM_NODE_NOT_FOUND, fsmType, status, role.getId(), opType);
        }
        return fsmNode;
    }

    /**
     * 通过code获取离线配置
     *
     * @param jobCodes
     * @return
     */
    private HashSet<JobConfig> getJobConfigByCodes(String jobCodes) {
        HashSet<JobConfig> jobConfigSet = new HashSet<>();
        if (jobCodes != null && !"".equals(jobCodes)) {
            String[] jobs = jobCodes.split(",");
            for (String job : jobs) {
                for (JobConfig jobConfig : jobConfigs) {
                    if (job.trim().equals(jobConfig.getJobCode())) {
                        jobConfigSet.add(jobConfig);
                        break;
                    }
                }
            }
        }
        return jobConfigSet;
    }


    /**
     * 通过code获取事务消息配置
     *
     * @param codes
     * @return
     */
    private HashSet<TransactionConfig> getTransactionConfigByCodes(String codes) {
        HashSet<TransactionConfig> configSet = new HashSet<>();
        if (codes != null && !"".equals(codes)) {
            String[] codeArr = codes.split(",");
            for (String code : codeArr) {
                for (TransactionConfig config : transactionConfigs) {
                    if (code.trim().equals(config.getCode())) {
                        configSet.add(config);
                        break;
                    }
                }
            }
        }
        return configSet;
    }

    /**
     * 小错误返回false打日志，严重问题直接throw Exception
     *
     * @param fsmMappings
     * @return
     */
    private void validationConfig(List<FsmMapping> fsmMappings) {
        if (fsmMappings == null || fsmMappings.isEmpty()) {
            throw new FsmException(ErrorCode.FSM_CONFIT_NOT_FOUND);
        }
        HashSet<String> configSet = new HashSet<>();
        for (FsmMapping fsmMapping : fsmMappings) {
            String key = String.format("%s_%s_%s_%s", fsmMapping.getFsmType(), fsmMapping.getRole(), fsmMapping.getSourceStatus(), fsmMapping.getOpType());
            if (configSet.contains(key)) {
                throw new FsmException(ErrorCode.FSM_CONFIG_REPEAT, fsmMapping.toString());
            }
            configSet.add(key);
        }


    }

    /******************set方法*******************/

    public void setFsmSelector(FsmSelector fsmSelector) {
        this.fsmSelector = fsmSelector;
    }

    public void setActionProvider(ActionProvider actionProvider) {
        this.actionProvider = actionProvider;
    }

    public void setFsmMappingProvider(FsmMappingProvider fsmMappingProvider) {
        this.fsmMappingProvider = fsmMappingProvider;
    }

    public void setUseTransactionConfig(boolean useTransactionConfig) {
        this.useTransactionConfig = useTransactionConfig;
    }

    public void setTransactionConfigProvider(TransactionConfigProvider transactionConfigProvider) {
        this.transactionConfigProvider = transactionConfigProvider;
    }

    public void setTransactionMsgProvider(TransactionMsgProvider transactionMsgProvider) {
        this.transactionMsgProvider = transactionMsgProvider;
    }

    public void setUseJob(boolean useJob) {
        this.useJob = useJob;
    }

    public void setJobConfigProvider(JobConfigProvider jobConfigProvider) {
        this.jobConfigProvider = jobConfigProvider;
    }


}
